#region

using System;
using System.Collections.Generic;
using System.IO;
using System.Text;

#endregion

namespace SubVisual
{
    internal class SvnRevProps
    {
        private static readonly int _utcOffset;

        private readonly string _filePath;
        public List<KeyValuePair<string, string>> Pairs = new List<KeyValuePair<string, string>>();

        static SvnRevProps()
        {
            _utcOffset = (int) DateTime.UtcNow.Subtract(DateTime.Now).TotalHours;
        }

        public SvnRevProps(string fileName)
        {
            if (string.IsNullOrEmpty(fileName))
            {
                throw new ArgumentException("Can't be null or Empty", "fileName");
            }
            if (!File.Exists(fileName))
            {
                throw new FileNotFoundException("Svn props file", fileName);
            }
            _filePath = fileName;
            using (var stream = File.OpenRead(_filePath))
            {
                KeyValuePair<string, string> pair;
                string header = GetHeader(stream);
                while (!string.IsNullOrEmpty(header) && header != "END")
                {
                    string[] headerParts = header.Split((char) 0x20);
                    int textChars = int.Parse(headerParts[1]);
                    string k = GetText(stream, textChars);
                    if (10 != stream.ReadByte())
                    {
                        throw new Exception("Error reading SvnRevProps file");
                    }
                    header = GetHeader(stream);
                    headerParts = header.Split((char) 0x20);
                    textChars = int.Parse(headerParts[1]);
                    string v = GetText(stream, textChars);
                    if (10 != stream.ReadByte())
                    {
                        throw new Exception("Error reading SvnRevProps file");
                    }
                    pair = new KeyValuePair<string, string>(k, v);
                    Pairs.Add(pair);
                    header = GetHeader(stream);
                }
            }
        }

        private string GetHeader(Stream stream)
        {
            var sb = new StringBuilder();
            byte b;
            while ((b = (byte) stream.ReadByte()) != 0x0A)
            {
                sb.Append(Encoding.Default.GetChars(new[] {b}));
            }
            return sb.ToString();
        }

        private string GetText(Stream stream, int textChars)
        {
            var bytes = new byte[textChars];
            var buffer = new byte[textChars];
            int totalRead = 0;
            int bytesRead;
            while (totalRead < textChars && 0 != (bytesRead = stream.Read(buffer, 0, buffer.Length)))
            {
                Array.Copy(buffer, 0, bytes, totalRead, bytesRead);
                totalRead += bytesRead;
            }
            return Encoding.Default.GetString(bytes);
        }

        public void SetDate(DateTime date)
        {
            //2008-10-22T04:50:56.056632Z
            string dateString = date.AddHours(_utcOffset).ToString("s") + ".000000Z";
            SetPropertyValue("svn:date", dateString);
        }

        public void SetAuthor(string author)
        {
            SetPropertyValue("svn:author", author);
        }

        private void SetPropertyValue(string key, string value)
        {
            KeyValuePair<string, string> pair = GetPair(key);
            int index = Pairs.IndexOf(pair);
            Pairs.Remove(pair);
            Pairs.Insert(index, new KeyValuePair<string, string>(key, value));
        }

        private KeyValuePair<string, string> GetPair(string key)
        {
            foreach (var pair in Pairs)
            {
                if (string.Compare(pair.Key, key, false) == 0)
                {
                    return pair;
                }
            }
            throw new ArgumentOutOfRangeException("key", string.Format("Couldn't find key {0}", key));
        }

        public void Save()
        {
            using (var stream = File.Open(_filePath, FileMode.Open, FileAccess.Write))
            {
                foreach (var pair in Pairs)
                {
                    WriteText(stream, "K {0}", pair.Key);
                    WriteText(stream, "V {0}", pair.Value);
                }
                WriteText(stream, "END");
            }
        }

        private void WriteText(Stream stream, string headerFormat, string text)
        {
            string header = string.Format(headerFormat, text.Length);
            WriteText(stream, header);
            WriteText(stream, text);
        }

        private void WriteText(Stream stream, string text)
        {
            byte[] bytes = Encoding.Default.GetBytes(text);
            stream.Write(bytes, 0, bytes.Length);
            stream.WriteByte(0x0A);
        }
    }
}